//INI file parser
// Created: Daly 2012-04-03
//

#include "ax_common.h"
#include "ax_ini.h"
#include <algorithm>

AxIniParser::AxIniParser()
{
	_err = 0;
}

static char* trim_space(char* s)
{
	char* p;
	char* q;
	p = s;
	q = s + strlen(s);
	//trim right
	while(q > s && (*q == ' ' || *q == '\t' || *q == '\n')) {
		*q = '\0';
		q--;
	}
	//trim left
	while(*p != '\0' && (*p == ' ' || *p == '\t' || *p == '\n')) p++;
	return p;
}

//find char or comment char
static char* find_char(const char* s, char c)
{
	while(*s && *s != c && *s != ';' && *s != '#') {
		s++;
	}
	return (char*)s;
}

//@return   0 parse ok  -1 file not exist  -2 parse error
int AxIniParser::parse(const char* file)
{
	FILE *fp;
	static char line[256] ;
	static char section[64] = "";
	static char name[64] = "";
	static char value[128] = ""; 
	char* p;
	char* start;
	char* end;
	int lineno = 0;

	if ((fp = fopen(file, "r")) == 0) {
		return -1;
	}
	//scan line by line	
	while (fgets(line, sizeof(line), fp))
	{
		lineno++;
		//trim space
		start = trim_space(line);
		//comment
		if (*start == ';' || *start == '#' || *start == '\0') {
			continue;
		} else if (*start == '[') {
			end = find_char(start+1, ']');
			if (*end == ']') {
				*end = '\0';
				strncpy(section, start+1, sizeof(section));
			} else {
				_err = lineno;
				return -2;
			}
		} else if (*start && *start != ';' && *start != '#') {
			end = find_char(start+1, '=');
			if (*end == '=') {
				//split value and name
				p = start;
				while(p < end && *p != ' ' && *p != '\t') p++; 	
				strncpy(name, start, p-start);
				name[p-start] = '\0';
				start = trim_space(end+1);
				p = start;
				while(*p != '\0' && *p != ' ' && *p != '\t') p++;	
				strncpy(value, start, p-start);
				value[p-start] = '\0';
				_values[ makekey(section, name) ] = value;
			} else {
				_err = lineno;
				return -2;
			}
		} 	
	}
	return 0;
}

std::string AxIniParser::makekey(const char* sec, const char* name)
{
	static char buf[1024];
	std::string key;
	sprintf(buf, "%s##%s", sec, name);	
	key = std::string(buf);
	std::transform(key.begin(), key.end(), key.begin(), ::tolower);
	return key;
}

//no value or invalid int return 0
int AxIniParser::get_int(const char* sec, const char* name)
{
	std::string key = makekey(sec, name);
	if (!_values.count(key)) return 0;
	return atoi(_values[key].c_str());
}

const char* AxIniParser::get_string(const char* sec, const char* name)
{
	std::string key = makekey(sec, name);	
	return _values.count(key) ? _values[key].c_str() : "";
}


IMPLEMENT_UTEST(ax_ini)
{
	int ret;
	AxIniParser p;
	ret = p.parse("test.ini");	
	if (ret != 0) {
		printf("parse error %d lineno: %d", ret, p.get_error());
		return;
	}
	printf("%d", p.get_int("ROOT", "test_int"));
	printf("%s", p.get_string("ROOT", "test_str"));
}
